module tmds_coder(
        input            clk,
        input            rst_n,
        // TMDS interface
        input            de,
        input      [7:0] d,
        input            c0,
        input            c1,
        output reg [9:0] q_o
    );

    reg [7:0] d_0;
    reg de_0, de_1;
    reg c0_0, c0_1;
    reg c1_0, c1_1;
    reg [3:0] n1d_0;
    reg [8:0] q_m_1;
    reg [3:0] n0q_m_1;
    reg [3:0] n1q_m_1;
    reg [4:0] cnt0 = 5'b0;

    wire [3:0] n1d;
    wire [8:0] q_m_0;
    wire [8:0] q_m_xor_0;
    wire [8:0] q_m_xnor_0;
    wire [3:0] n1q_m_0;
    wire mux_0, mux_1;
    wire [9:0] qo_0;
    wire [9:0] qo_1;
    wire [4:0] nsub;
    wire [4:0] cnt1;

    genvar i;

    assign n1d = ((d[7] + d[6]) + (d[5] + d[4]))
               + ((d[3] + d[2]) + (d[1] + d[0]));

    always @(posedge clk) begin
        de_0  <= de;
        d_0   <= d;
        c0_0  <= c0;
        c1_0  <= c1;
        n1d_0 <= n1d;
    end

    assign q_m_xor_0[0] = d_0[0];
    for(i=1; i<8; i=i+1)
    begin : xor_for
        assign q_m_xor_0[i] = q_m_xor_0[i-1] ^ d_0[i];
    end
    assign q_m_xor_0[8] = 1'b1;

    assign q_m_xnor_0[0] = d_0[0];
    for(i=1; i<8; i=i+1)
    begin : xnor_for
        assign q_m_xnor_0[i] = q_m_xnor_0[i-1] ^~ d_0[i];
    end
    assign q_m_xnor_0[8] = 1'b0;

    assign q_m_0 = ((n1d_0>4) | ((n1d_0==4) & (!d_0[0]))) ? q_m_xnor_0 : q_m_xor_0;

    assign n1q_m_0 = ((q_m_0[7] + q_m_0[6]) + (q_m_0[5] + q_m_0[4]))
                   + ((q_m_0[3] + q_m_0[2]) + (q_m_0[1] + q_m_0[0]));

    always @(posedge clk) begin
        n1q_m_1 <= n1q_m_0;
        n0q_m_1 <= 8 - n1q_m_0;
        q_m_1   <= q_m_0;
        de_1    <= de_0;
        c0_1    <= c0_0;
        c1_1    <= c1_0;
    end

    assign mux_0 = ((cnt0==0) | (n1q_m_1==4)) ? 1'b1 : 1'b0;
    assign mux_1 = ((($signed(cnt0)>0) & (n1q_m_1>4)) | (($signed(cnt0)<0) & (n1q_m_1<4))) ? 1'b1 : 1'b0;

    assign qo_0 = ((!c0_1) & (!c1_1)) ? 10'b1101010100 :
                  ((!c0_1) & ( c1_1)) ? 10'b0101010100 :
                  (( c0_1) & (!c1_1)) ? 10'b0010101011 :
                  10'b1010101011;

    assign qo_1 = (!de_1)               ? qo_0                               :
                  (mux_0 & q_m_1[8])    ? {~q_m_1[8], q_m_1[8], q_m_1[7:0]}  :
                  (mux_0 & (!q_m_1[8])) ? {~q_m_1[8], q_m_1[8], ~q_m_1[7:0]} :
                  mux_1                 ? {1'b1, q_m_1[8], ~q_m_1[7:0]}      :
                  {1'b0, q_m_1[8], q_m_1[7:0]};

    assign nsub = n0q_m_1 - n1q_m_1;

    assign cnt1 = (!de_1)               ? 5'b0                           :
                  (mux_0 & q_m_1[8])    ? cnt0 - nsub                    :
                  (mux_0 & (!q_m_1[8])) ? cnt0 + nsub                    :
                  mux_1                 ? cnt0 + {q_m_1[8], 1'b0} + nsub :
                  cnt0 - {(~q_m_1[8]), 1'b0} - nsub;

    always @(posedge clk) begin
        q_o <= qo_1;
        cnt0 <= cnt1;
    end

endmodule 